/*
* INSANE - Interactive Structural Analysis Environment
*
* Copyright (C) 2003-2004
* Universidade Federal de Minas Gerais
* Escola de Engenharia
* Departamento de Engenharia de Estruturas
*
* Author's email : insane@dees.ufmg.br
* Author's Website : http://www.dees.ufmg.br/insane
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package br.ufmg.dees.insane.model.xfemModel.crackGrowthIncrementLaw;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import br.ufmg.dees.insane.model.femModel.elementNode.ElementNode;
import br.ufmg.dees.insane.model.xfemModel.enrichmentItem.CrackInterior;
import br.ufmg.dees.insane.model.xfemModel.enrichmentItem.CrackTip;
import br.ufmg.dees.insane.model.xfemModel.enrichmentItem.EnrichmentItem;
import br.ufmg.dees.insane.model.xfemModel.xfemElement.XFEMElement;
import br.ufmg.dees.insane.model.xfemModel.xfemelementNode.XFEMElementNode;
import br.ufmg.dees.insane.util.IPoint3d;
import br.ufmg.dees.insane.util.IVector;
import br.ufmg.dees.insane.util.xfem.Line;
import br.ufmg.dees.insane.util.xfem.Segment;

/**
 * @author Kelson Wolff
 *
 */
public class BoundaryElementIncrement extends CrackGrowthIncrementLaw {
	
	/**
	 * Comment for <code>serialVersionUID</code>
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * 
	 */
	public BoundaryElementIncrement() {
		super();
		// TODO Auto-generated constructor stub
	}

	@Override
	public double computeIncrementLength() {
		// TODO Auto-generated method stub
		return 0;
	}
	
	/**
	 * Gets the increment lenght of the crack that cross the provided
	 * XFEMElement tipElement in provided direction crackDirection.
	 * @param tipElement The element that contains the crack tip.
	 * @param direction The crack direction.
	 * @return The increment lenght of the crack that cross the provided
	 * XFEMElement tipElement in provided direction crackDirection.
	 */
	public double getIncrement(XFEMElement tipElement, IVector crackDirection){
		double xOrigin = 0.0;
		double yOrigin = 0.0;
		ListIterator enrItems = tipElement.getEnrichmentItemList().listIterator();
		while(enrItems.hasNext()){
			EnrichmentItem enrItem = (EnrichmentItem) enrItems.next();
			if(enrItem instanceof CrackTip){
				CrackTip cTip = (CrackTip) enrItem;
				xOrigin = cTip.getTip().getCoordinates(0);
				yOrigin = cTip.getTip().getCoordinates(1);
			}
			if(enrItem instanceof CrackInterior){
				CrackInterior cInterior = (CrackInterior) enrItem;
				int i = cInterior.getMyTips().size();
				xOrigin = cInterior.getMyTips().get(i-1).getTip().getCoordinates(0);
				yOrigin = cInterior.getMyTips().get(i-1).getTip().getCoordinates(1);
			}
		}
		IPoint3d origin = new IPoint3d(xOrigin,yOrigin);
		IPoint3d vector = new IPoint3d(crackDirection.getElement(0),crackDirection.getElement(1));
		Line crackLine = new Line(origin,vector);
		
		List<IPoint3d> pts = new ArrayList<IPoint3d>();
		List<ElementNode> nodesList = tipElement.getIncidence();
		ListIterator nodes = nodesList.listIterator();
		while(nodes.hasNext()){
			XFEMElementNode node = (XFEMElementNode) nodes.next();
			IPoint3d p = new IPoint3d(node.getX(),node.getY());
			pts.add(p);
		}
		
		IPoint3d intersection = new IPoint3d();
		boolean found = false;
		Segment s = new Segment();
		int i = 0;
		while((i<pts.size()) && (!found)){
			if(i<(pts.size()-1)){
				s.setFirstPoint((IPoint3d)pts.get(i));
				s.setSecondPoint((IPoint3d)pts.get(i+1));
			}
			if(i == (pts.size()-1)){
				s.setFirstPoint((IPoint3d)pts.get(i));
				s.setSecondPoint((IPoint3d)pts.get(0));
			}
			if(crackLine.intersects(s)){
				found = true;
				intersection = crackLine.intersection(s);
			}
			i++;
		}
		
		return origin.distance(intersection);
	}
	
	/**
	 * Gets the updated crack tip point computed from actual crack
	 * that exists in the provided XFEMElement tipElement. The new
	 * crack tip point is computed in provided direction crackDirection.
	 * @param tipElement The element that contains the actual crack tip.
	 * @param direction The crack direction for new crack position.
	 * @return The new position (point) of the crack tip that cross the 
	 * provided XFEMElement tipElement in provided direction crackDirection.
	 */
	public IPoint3d getCrackTip(XFEMElement tipElement, IVector crackDirection){
		double xOrigin = 0.0;
		double yOrigin = 0.0;
		IPoint3d oldTip = null;
		IPoint3d actualTip = null;
		ListIterator enrItems = tipElement.getEnrichmentItemList().listIterator();
		while(enrItems.hasNext()){
			EnrichmentItem enrItem = (EnrichmentItem) enrItems.next();
			if(enrItem instanceof CrackTip){
				CrackTip cTip = (CrackTip) enrItem;
				xOrigin = cTip.getTip().getCoordinates(0);
				yOrigin = cTip.getTip().getCoordinates(1);
				actualTip = new IPoint3d(xOrigin,yOrigin);
				double xOldTip = cTip.getTipSegment().getFirstPoint().getX();
				double yOldTip = cTip.getTipSegment().getFirstPoint().getY();
				oldTip = new IPoint3d(xOldTip,yOldTip);
			}
		}
		IPoint3d origin = new IPoint3d(xOrigin,yOrigin);
		IPoint3d vector = new IPoint3d(crackDirection.getElement(0),crackDirection.getElement(1));
		Line crackLine = new Line(origin,vector);
		
		List<IPoint3d> pts = new ArrayList<IPoint3d>();
		List<ElementNode> nodesList = tipElement.getIncidence();
		ListIterator nodes = nodesList.listIterator();
		while(nodes.hasNext()){
			XFEMElementNode node = (XFEMElementNode) nodes.next();
			IPoint3d p = new IPoint3d(node.getX(),node.getY());
			pts.add(p);
		}
		List<IPoint3d> intersections1 = new ArrayList<IPoint3d>();
		Segment s = new Segment();
		int i = 0;
		while(i<pts.size()){
			if(i<(pts.size()-1)){
				s.setFirstPoint((IPoint3d)pts.get(i));
				s.setSecondPoint((IPoint3d)pts.get(i+1));
			}
			if(i == (pts.size()-1)){
				s.setFirstPoint((IPoint3d)pts.get(i));
				s.setSecondPoint((IPoint3d)pts.get(0));
			}
			double x = s.getSecondPoint().getX()-s.getFirstPoint().getX();
			double y = s.getSecondPoint().getY()-s.getFirstPoint().getY();
			s.setVector(new IPoint3d(x,y));
			
			if(crackLine.intersects(s)){
				IPoint3d intersect = new IPoint3d();
				intersect = crackLine.intersection(s);
				intersections1.add(intersect);
			}
			i++;
		}
		
		double tolerance = 1e-6;
		List<IPoint3d> intersections2 = new ArrayList<IPoint3d>();
		Segment s1 = new Segment(actualTip,oldTip);
		Segment s2 = null;
		ListIterator intersects = intersections1.listIterator();
		while(intersects.hasNext()){
			IPoint3d intersecPoint = (IPoint3d) intersects.next();
			s2 = new Segment(actualTip,intersecPoint);
			double dotProduct = s2.getVector().getX()*s1.getVector().getX() + s2.getVector().getY()*s1.getVector().getY();
			if(dotProduct < 0 && dotProduct < -tolerance){
				intersections2.add(intersecPoint);
			}
		}
		
		IPoint3d intersection = null;
		if(intersections2.size() == 1){
			intersection = intersections2.get(0);
		}
		else{
			int lastIndex = intersections2.size() - 1;
			double x = intersections2.get(lastIndex).getX();
			double y = intersections2.get(lastIndex).getY();
			intersection = new IPoint3d( Math.floor(x*1e6+0.5)/1e6 , Math.floor(y*1e6+0.5)/1e6 );
		}
		
		return intersection;
	}

}
